<?php

namespace app\xcxapi\controller\v1;

use app\common\controller\XcxController;

if (version_compare(PHP_VERSION, '5.1.2') < 0) {
    trigger_error('need php 5.1.2 or newer', E_USER_ERROR);
}
class GetUserAPI extends XcxController
{
    private $key;
    private $sdkappid;

    public function __construct()
    {
        $this->sdkappid = 1400315147;
        $this->key = 'a851f96464b8acdbf53fbe717157c3c8b1c3cb4f2cfac48e7e37e0ce07bdd94e';
    }

    /**
     * 生成签名。
     *
     * @param $identifier 用户账号
     * @param int $expire 过期时间，单位秒，默认 180 天
     * @param $userbuf base64 编码后的 userbuf
     * @param $userbuf_enabled 是否开启 userbuf
     *
     * @throws \Exception
     *
     * @return string 签名字符串
     */
    private function __genSig($identifier, $expire, $userbuf, $userbuf_enabled)
    {
        $expire = $expire * 86400;
        $curr_time = time();
        $sig_array = [
            'TLS.ver' => '2.0',
            'TLS.identifier' => (string) $identifier,
            'TLS.sdkappid' => (int) ($this->sdkappid),
            'TLS.expire' => (int) $expire,
            'TLS.time' => (int) $curr_time,
        ];
        $base64_userbuf = '';
        if (true === $userbuf_enabled) {
            $base64_userbuf = base64_encode($userbuf);
            $sig_array['TLS.userbuf'] = (string) $base64_userbuf;
        }
        $sig_array['TLS.sig'] = $this->hmacsha256($identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled);
        if (false === $sig_array['TLS.sig']) {
            throw new \Exception('base64_encode error');
        }
        $json_str_sig = json_encode($sig_array);
        if (false === $json_str_sig) {
            throw new \Exception('json_encode error');
        }
        $compressed = gzcompress($json_str_sig);
        if (false === $compressed) {
            throw new \Exception('gzcompress error');
        }

        return $this->base64_url_encode($compressed);
    }

    /**
     * 验证签名。
     *
     * @param string $sig         签名内容
     * @param string $identifier  需要验证用户名，utf-8 编码
     * @param int    $init_time   返回的生成时间，unix 时间戳
     * @param int    $expire_time 返回的有效期，单位秒
     * @param string $userbuf     返回的用户数据
     * @param string $error_msg   失败时的错误信息
     *
     * @throws \Exception
     *
     * @return bool 验证是否成功
     */
    private function __verifySig($sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg)
    {
        try {
            $error_msg = '';
            $compressed_sig = $this->base64_url_decode($sig);
            $pre_level = error_reporting(E_ERROR);
            $uncompressed_sig = gzuncompress($compressed_sig);
            error_reporting($pre_level);
            if (false === $uncompressed_sig) {
                throw new \Exception('gzuncompress error');
            }
            $sig_doc = json_decode($uncompressed_sig);
            if (false === $sig_doc) {
                throw new \Exception('json_decode error');
            }
            $sig_doc = (array) $sig_doc;
            if ($sig_doc['TLS.identifier'] !== $identifier) {
                throw new \Exception("identifier dosen't match");
            }
            if ($sig_doc['TLS.sdkappid'] !== $this->sdkappid) {
                throw new \Exception("sdkappid dosen't match");
            }
            $sig = $sig_doc['TLS.sig'];
            if (false === $sig) {
                throw new \Exception('sig field is missing');
            }
            $init_time = $sig_doc['TLS.time'];
            $expire_time = $sig_doc['TLS.expire'];
            $curr_time = time();
            if ($curr_time > $init_time + $expire_time) {
                throw new \Exception('sig expired');
            }
            $userbuf_enabled = false;
            $base64_userbuf = '';
            if (isset($sig_doc['TLS.userbuf'])) {
                $base64_userbuf = $sig_doc['TLS.userbuf'];
                $userbuf = base64_decode($base64_userbuf, true);
                $userbuf_enabled = true;
            }
            $sigCalculated = $this->hmacsha256($identifier, $init_time, $expire_time, $base64_userbuf, $userbuf_enabled);
            if ($sig !== $sigCalculated) {
                throw new \Exception('verify failed');
            }

            return true;
        } catch (\Exception $ex) {
            $error_msg = $ex->getMessage();

            return false;
        }
    }

    /**
     * 生成签名.
     *
     * @param $identifier 用户账号
     * @param int $expire 过期时间，单位秒，默认 180 天
     *
     * @throws \Exception
     *
     * @return string 签名字符串
     */
    public function genSig($identifier, $expire = 180)
    {
        return $this->__genSig($identifier, $expire, '', false);
    }

    /**
     * 带 userbuf 生成签名。
     *
     * @param $identifier 用户账号
     * @param int    $expire  过期时间，单位秒，默认 180 天
     * @param string $userbuf 用户数据
     *
     * @throws \Exception
     *
     * @return string 签名字符串
     */
    public function genSigWithUserBuf($identifier, $expire, $userbuf)
    {
        return $this->__genSig($identifier, $expire, $userbuf, true);
    }

    /**
     * 带 userbuf 验证签名。
     *
     * @param string $sig         签名内容
     * @param string $identifier  需要验证用户名，utf-8 编码
     * @param int    $init_time   返回的生成时间，unix 时间戳
     * @param int    $expire_time 返回的有效期，单位秒
     * @param string $error_msg   失败时的错误信息
     *
     * @throws \Exception
     *
     * @return bool 验证是否成功
     */
    public function verifySig($sig, $identifier, &$init_time, &$expire_time, &$error_msg)
    {
        $userbuf = '';

        return $this->__verifySig($sig, $identifier, $init_time, $expire_time, $userbuf, $error_msg);
    }

    /**
     * 验证签名.
     *
     * @param string $sig         签名内容
     * @param string $identifier  需要验证用户名，utf-8 编码
     * @param int    $init_time   返回的生成时间，unix 时间戳
     * @param int    $expire_time 返回的有效期，单位秒
     * @param string $userbuf     返回的用户数据
     * @param string $error_msg   失败时的错误信息
     *
     * @throws \Exception
     *
     * @return bool 验证是否成功
     */
    public function verifySigWithUserBuf($sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg)
    {
        return $this->__verifySig($sig, $identifier, $init_time, $expire_time, $userbuf, $error_msg);
    }

    /**
     * User: wufeng
     * Date: 2020/02/13.
     *
     * @throws \Exception
     *
     * @return string
     */
    public function getUserSig()
    {
		$list['data'] = $this->genSig(input('userid'));
		$list['msg'] = 'success';
		$list['code'] = 1;
		$this->win($list);
    }

    /**
     * 用于 url 的 base64 encode
     * '+' => '*', '/' => '-', '=' => '_'.
     *
     * @param string $string 需要编码的数据
     *
     * @throws \Exception
     *
     * @return string 编码后的base64串，失败返回false
     */
    private function base64_url_encode($string)
    {
        static $replace = [
            '+' => '*',
            '/' => '-',
            '=' => '_',
        ];
        $base64 = base64_encode($string);
        if (false === $base64) {
            throw new \Exception('base64_encode error');
        }

        return str_replace(array_keys($replace), array_values($replace), $base64);
    }

    /**
     * 用于 url 的 base64 decode
     * '+' => '*', '/' => '-', '=' => '_'.
     *
     * @param string $base64 需要解码的base64串
     *
     * @throws \Exception
     *
     * @return string 解码后的数据，失败返回false
     */
    private function base64_url_decode($base64)
    {
        static $replace = [
            '+' => '*',
            '/' => '-',
            '=' => '_',
        ];
        $string = str_replace(array_values($replace), array_keys($replace), $base64);
        $result = base64_decode($string, true);
        if (false === $result) {
            throw new \Exception('base64_url_decode error');
        }

        return $result;
    }

    /**
     * 使用 hmac sha256 生成 sig 字段内容，经过 base64 编码
     *
     * @param $identifier 用户名，utf-8 编码
     * @param $curr_time 当前生成 sig 的 unix 时间戳
     * @param $expire 有效期，单位秒
     * @param $base64_userbuf base64 编码后的 userbuf
     * @param $userbuf_enabled 是否开启 userbuf
     *
     * @return string base64 后的 sig
     */
    private function hmacsha256($identifier, $curr_time, $expire, $base64_userbuf, $userbuf_enabled)
    {
        $content_to_be_signed = 'TLS.identifier:'.$identifier."\n"
            .'TLS.sdkappid:'.$this->sdkappid."\n"
            .'TLS.time:'.$curr_time."\n"
            .'TLS.expire:'.$expire."\n";
        if (true === $userbuf_enabled) {
            $content_to_be_signed .= 'TLS.userbuf:'.$base64_userbuf."\n";
        }

        return base64_encode(hash_hmac('sha256', $content_to_be_signed, $this->key, true));
    }
}
